package com.happe.foodeasy.winter.typemap;

import com.happe.foodeasy.helper.AnnotationHelper;
import com.happe.foodeasy.helper.ClassOperateHelper;
import com.happe.foodeasy.winter.conf.parser.DatabaseTypeMapParseResult;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * This class is used to deal with mapping java type to database type.
 * Created by wubin on 2016-05-20.
 */
@Component
public class ParseTypeMap {
    private static final String MAP_TYPE_SUFFIX = "TypeMapDeal";
    private static final String DEAL_METHOD_NAME = "retrieveMapType";
    private static final String SET_TYPE_PARSE_RESULT_METHOD = "setDatabaseTypeParseResult";
    private static final String BYTE_ARRAY_TYPE_NAME = "ByteArray";

    /**
     * This method is used to deal with mapping java type to database type, and has some default behavior,
     * 1) assume the real class of typological dispose is the same package with ParseTypeMap class.
     * 2) assume the real class of typological dispose is named by java type name and appends suffix TypeMapDeal.
     * 3) assume the real class of typological dispose's deal method's name is retrieveMapType.
     *
     * @param field the target field should be transformed.
     * @return the database type name corresponding target java type name.
     */
    public String startTypeMap(Field field, DatabaseTypeMapParseResult typeMapParseResult) {
        String typeName;
        boolean isForeignKey = AnnotationHelper.checkRelationalAnnotationForField(field);

        if (isForeignKey) {
            typeName = "Number";
        } else if (ClassOperateHelper.checkTargetClassIsByteArray(field.getType())) {
            typeName = BYTE_ARRAY_TYPE_NAME;
        } else if (field.getType().isPrimitive()) {
            typeName = dealPrimitiveType(field);
        } else {
            typeName = field.getType().getSimpleName();
        }

        String targetType;
        Class<?> targetClass = findTargetClassByTypeName(typeName);
        try

        {
            Object targetEntity = targetClass.newInstance();
            //invoke setDatabaseTypeParseResult method to set DatabaseTypeMapParseResult object.
            Method setTemplateMethod = targetClass.getMethod(SET_TYPE_PARSE_RESULT_METHOD, DatabaseTypeMapParseResult.class);
            setTemplateMethod.invoke(targetEntity, typeMapParseResult);

            //invoke retrieveMapType method to return the result of dispose.
            Method dealMethod = targetClass.getMethod(DEAL_METHOD_NAME, Field.class);
            targetType = (String) dealMethod.invoke(targetEntity, field);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            throw new RuntimeException(e);
        }

        return targetType;
    }

    private String dealPrimitiveType(Field field) {
        String typeName = field.getType().getSimpleName();
        if (isNumber(typeName)) {
            return Number.class.getSimpleName();
        } else if (typeName.equals("boolean")) {
            return "Boolean";
        }
        throw new RuntimeException("无法处理" + typeName + "类型");
    }

    private boolean isNumber(String typeName) {
        return typeName.equals("int") || typeName.equals("double") || typeName.equals("long") || typeName.equals("float");
    }

    private static Class findTargetClassByTypeName(String typeName) {
        Class[] allClassInTargetPack = ClassOperateHelper.findClassInTargetPackage(ParseTypeMap.class.getPackage().getName());
        for (Class oneClass : allClassInTargetPack) {
            String className = oneClass.getSimpleName();
            if (className.equals(typeName + MAP_TYPE_SUFFIX))
                return oneClass;
        }

        throw new RuntimeException("对于" + typeName + "，没有找到对应的处理类型");
    }
}
